#include <iostream>
#include "sendsignal_f_study.h"
#include <math.h>
#include <float.h>
#include <stdexcept> // std::exception
#include <algorithm>

#include "calcError.h"

sendsignal_f_study::sendsignal_f_study() {
}

sendsignal_f_study::~sendsignal_f_study() {
}

void sendsignal_f_study::setstudysignalid(ntype::signalid_t id) {
  signalid = id;
}

ntype::signalid_t sendsignal_f_study::getsignalid()
{
  return signalid;
}

void sendsignal_f_study::setinsignal(ntype::signal_t* signal, int) {
  const int neuron_in_layer       = layersize;
  int       synapse_count         = 0;
  synapse   syn_val;
/* устанавливаем сигнал */
  for(int index = 0; index < neuron_in_layer; index ++) {
    for(synapse_count = 0; synapse_count < neuron_in_layer; synapse_count ++) {
      syn_val = p_neuron[index].getSynapse(synapse_count);
      syn_val.setFSignalStudy(signalid, signal[synapse_count]);
      p_neuron[index].setSynapse(syn_val, synapse_count);
    }
  }
}

void sendsignal_f_study::send(double* , int ) {
  const int neuron_in_layer       = layersize;
  const int layers_count          = getlayercountmax();
  int       start_index           = 0;
  int       start_prev_index      = 0;
  int       index                 = 0;
  int       prev_index            = 0;
  int       layer                 = 0;
  int       synapse_count         = 0;
  synapse   syn_val;
  axon      axon_val;
  double    signal                = 0.;

/* вычисляем нейроны нулевого (входного) слоя */
  for(index = 0; index < neuron_in_layer; index ++) {
    p_neuron[index].setStudyBufferIndex(signalid);
    (void)p_neuron[index].calc_study();
  }

/* вычисляем остальные слои */
  for(layer = 1; layer < layers_count; layer ++)
  {
    start_index      = getstartneuronidforlayer(layer);
	start_prev_index = getstartneuronidforlayer(layer-1);
    for(index = 0; index < neuron_in_layer; index ++)
	{
/*обновляем синапсы */
      for(synapse_count = 0; synapse_count < neuron_in_layer; synapse_count++)
	  {
        prev_index = start_prev_index + synapse_count;
        axon_val = p_neuron[prev_index].getAxon();
        signal = axon_val.getFStudySignal(signalid);
		syn_val = p_neuron[index + start_index].getSynapse(synapse_count);
		syn_val.setFSignalStudy(signalid, signal);
        p_neuron[index + start_index].setSynapse(syn_val, synapse_count);

	  }
	}
/* вычисляем нйроны для данного слоя */
    for(index = 0; index < neuron_in_layer; index ++) {
      p_neuron[index + start_index].setStudyBufferIndex(signalid);
      p_neuron[index + start_index].calc_study();
    }
  }
}


#if 0
bool sendsignal_f_study::checkErrorValue(ntype::signal_t in, ntype::signal_t exp, ntype::error_t err_range, ntype::signal_t min, ntype::signal_t max)
{
    ntype::signal_t max_rage = max - (max - max * err_range);
    ntype::signal_t min_rage = min - (min - min * err_range);
    ntype::signal_t max_sig = exp + max_rage;
    ntype::signal_t min_sig = exp + min_rage;
    return((in < max_sig) && (in > min_sig )) ?  true : false;
}
#endif

  ntype::error_t sendsignal_f_study::geterror(const ntype::signal_t *exp_sig) {
    const int in_size      = layersize;
    const int layers_count = getlayercountmax();
    const int start_index  = getstartneuronidforlayer(layers_count -1);
    double    ret_error    = 0.;
    double    calc_val;
    ntype::signal_t    local_exp_sig;
    ntype::signal_t    outsig;

    for(int icount = 0; icount < in_size; icount ++)
    {
//      insig = p_neuron[0].getSynapse(icount).getFSignalStudy(signalid);
      local_exp_sig = exp_sig[icount];
      outsig = p_neuron[start_index + icount].getAxon().getFStudySignal(signalid);

      local_exp_sig = (fabs(local_exp_sig) <= MIN_VALUE) ? ZERO_VALUE : local_exp_sig;
      outsig = (fabs(outsig) <= MIN_VALUE) ? ZERO_VALUE : outsig;

      calc_val = getSignalErrorValueAbs(outsig, local_exp_sig, ALLOWED_ERROR_RANGE);
      ret_error += getSignalErrorValueAbs(outsig, local_exp_sig, ALLOWED_ERROR_RANGE);

#if 0
      if(checkErrorValue(outsig, local_exp_sig, ALLOWED_ERROR_RANGE) == true)
      {
          calc_val = ZERO_VALUE;
      }
//      else if(fabs(local_exp_sig) <= ZERO_VALUE && fabs(outsig) <= ZERO_VALUE)
//          calc_val = ZERO_VALUE;
      else
      {
        calc_val = ((outsig - local_exp_sig) / std::max(outsig, local_exp_sig));
        calc_val = fabs(calc_val);
      }
      ret_error += calc_val;
#endif
//std::cout << "sig_id: "<< signalid <<" calc err: " << calc_val << " sum error: "<< ret_error << " exp_sig: "<< local_exp_sig << " out_sig: " <<  outsig << std::endl;
//getLog()->print("err: -> %f  : in_sig -> %f : out_sig -> %f\n", ret_error, insig, outsig);
  }
  ret_error = ret_error / in_size;
  return ret_error < MIN_ERROR_VALUE ? ZERO_VALUE : ret_error;
}
